package com.miniapp.interceptor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.miniapp.handler.exception.SqlInjectionException;
import com.miniapp.properties.SqlInjectionProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 支持白名单配置的SQL注入防护拦截器
 * @author author
 * @date 2025-09-01 10:34
 */
public class SqlInjectionInterceptor implements HandlerInterceptor {

    @Autowired
    private SqlInjectionProperties sqlInjectionProperties;

    private final Pattern sqlInjectionPattern;
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    public SqlInjectionInterceptor() {
        // 初始化默认正则表达式
        this.sqlInjectionPattern = Pattern.compile(".*('|;|\\+\\+|\\-\\-|\\*\\*|\\/\\/|\\s+(union|select|insert|delete|update|drop|alter|exec|xp_)\\s+).*", Pattern.CASE_INSENSITIVE);
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 如果未启用SQL注入防护，直接放行
        if (!sqlInjectionProperties.isEnabled()) {
            return true;
        }
        // 检查当前URL是否在白名单中
        String requestUri = request.getRequestURI();
        if (isWhitelistUrl(requestUri)) {
            return true;
        }
        // 检查URL参数
        checkRequestParameters(request);
        // 检查请求体（JSON参数）
        checkRequestBody(request);
        return true;
    }

    /**
     * 检查当前URL是否在白名单中
     */
    private boolean isWhitelistUrl(String requestUri) {
        return sqlInjectionProperties.getWhitelistUrls().stream()
                .anyMatch(pattern -> pathMatcher.match(pattern, requestUri));
    }

    /**
     * 检查参数是否在白名单中
     */
    private boolean isWhitelistParam(String paramName) {
        return sqlInjectionProperties.getWhitelistParams().contains(paramName);
    }

    /**
     * 检查URL参数是否存在SQL注入风险
     */
    private void checkRequestParameters(HttpServletRequest request) {
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            // 如果是白名单参数，跳过检查
            if (isWhitelistParam(paramName)) {
                continue;
            }

            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues != null) {
                for (String value : paramValues) {
                    if (StrUtil.isNotBlank(value) && isSqlInjection(value)) {
                        throw new SqlInjectionException(paramName, value);
                    }
                }
            }
        }
    }

    /**
     * 检查请求体（JSON）是否存在SQL注入风险
     */
    private void checkRequestBody(HttpServletRequest request) {
        if (!(request instanceof ContentCachingRequestWrapper)) {
            return;
        }

        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
        byte[] content = wrapper.getContentAsByteArray();
        if (content == null || content.length == 0) {
            return;
        }

        String contentType = request.getContentType();
        // 只处理JSON类型的请求体
        if (contentType != null && contentType.contains("application/json")) {
            String jsonStr = new String(content);
            if (JSONUtil.isTypeJSON(jsonStr)) {
                JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
                checkJsonNode(jsonObject, "");
            }
        }
    }

    /**
     * 递归检查JSON节点
     */
    private void checkJsonNode(JSONObject jsonObject, String parentKey) {
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            String key = entry.getKey();
            String fullKey = StrUtil.isBlank(parentKey) ? key : parentKey + "." + key;

            // 如果是白名单参数，跳过检查
            if (isWhitelistParam(key) || isWhitelistParam(fullKey)) {
                continue;
            }

            Object value = entry.getValue();
            if (value instanceof String) {
                String strValue = (String) value;
                if (StrUtil.isNotBlank(strValue) && isSqlInjection(strValue)) {
                    throw new SqlInjectionException(fullKey, strValue);
                }
            } else if (value instanceof JSONObject) {
                // 递归检查嵌套JSON
                checkJsonNode((JSONObject) value, fullKey);
            }
        }
    }

    /**
     * 判断字符串是否包含SQL注入风险
     */
    private boolean isSqlInjection(String value) {
        // 使用配置的正则表达式进行匹配
        return sqlInjectionPattern.matcher(value).matches();
    }
}